--[[
AIOSkillshots revamped
]]
--Settings----------------------------------------------
local showSkillShotRange = false -- change this to true to enable a range indicator
local showPredictions = true -- change this to false to disable showing circles on the predicted locations of your targets
local disableMinionCollisionDetection = false -- change this to true to disable minion collision detection (can reduce lag)
local autoTargetSelector = true -- change this to false to enable manual target selection (hover your mouse over the target before you press the hotkey)
local triggerKeyQ = 90 -- hotkey: z or Z (http://tinyurl.com/3o99xo2)
local triggerKeyW = 88 -- hotkey: x or X (http://tinyurl.com/3o99xo2)
local triggerKeyE = 67 -- hotkey: c or C (http://tinyurl.com/3o99xo2)
local toggleKeyR = 86 -- hotkey: v or V (http://tinyurl.com/3o99xo2)
local spamKey = 32 -- hotkey: space (http://tinyurl.com/3o99xo2)
local smoothness = 70 --value from 0(not smooth) to 99 really smooth prediction
local BasicAttackSpell = "q" --Attack automatically the Target
--------------------------------------------------------

--Code (Don't touch anything below this line!)----------
local SCRIPT_PATH = debug.getinfo(1).source:sub(debug.getinfo(1).source:find(".*\\")):sub(2)
local player = GetMyHero()
local spellArray = {} -- spellLetter.properties
dofile(SCRIPT_PATH .. "libs/target_prediction.lua") --use last Version by gReY
dofile(SCRIPT_PATH .. "libs/target_selector.lua") --I have to optimize this one to return a table with all enemies in order of priority.

function TargetHaveBuff(buffName, target)
    assert(type(buffName) == "string" or type(buffName) == "table", "TargetHaveBuff: wrong argument types (<string> or <table of string> expected for buffName)")
    local function isBuffValid(buff)
        return buff and buff.name and buff.startT <= GetGameTimer() and buff.endT >= GetGameTimer()
    end

    local target = target or player
    for i = 1, target.buffCount do
        local tBuff = target:getBuff(i)
        if isBuffValid(tBuff) then
            if type(buffName) == "string" then
                if tBuff.name:lower() == buffName:lower() then return true end
            else
                for j, sBuff in ipairs(buffName) do
                    if tBuff.name:lower() == sBuff:lower() then return true end
                end
            end
        end
    end
    return false
end
function OnTick()
    if not lastUpdated or os.clock() - lastUpdated > 0.2 then
        lastUpdated = os.clock()
        local function findHeroNearestMouse()
            local closestDistance, closest = math.huge, nil
            for i = 0, heroManager.iCount, 1 do
                local hero = heroManager:getHero(i)
                if target and target.visible and target.team == TEAM_ENEMY then
                    local currentDistance = math.sqrt((target.x - mousePos.x) ^ 2 + (target.z - mousePos.z) ^ 2)
                    if currentDistance < closestDistance then
                        closestDistance = currentDistance
                        closest = target
                    end
                end
            end
            return closest, closestDistance
        end

        local function hasShield(target)
            TargetHaveBuff({"SpellShield","bansheesveil","UndyingRage","JudicatorIntervention","NocturneShroudofDarkness"},target)
        end

        for k, v in pairs(spellArray) do --use spellArray[k] for things you want to change, v for constant things
            if v.active and (not continueTick or GetTickCount() >= continueTick + 10) and player:CanUseSpell(v.spellKey) == READY then
                --TargetSelection
                local prediction, minions, healthPrediction
                if v.speed then
                    if autoTargetSelector then
                        if v.target then prediction, minions, healthPrediction = tp:GetPrediction(v.target.name, v.range, v.speed, v.delay, v.width, 0) end --checks if he can attack last target(even if  not visible)
                        if not prediction or spellArray[k].target.dead then
                            spellArray[k].ts:tick()
                            spellArray[k].target = spellArray[k].ts.target
                            if spellArray[k].target then prediction, minions, healthPrediction = tp:GetPrediction(spellArray[k].target.name, v.range, v.speed, v.delay, v.width, 0) end
                        end
                    else
                        spellArray[k].target = findHeroNearestMouse()
                        if spellArray[k].target then prediction, minions = tp:GetPrediction(spellArray[k].target.name, v.range, v.speed, v.delay, v.width, 0) end
                    end
                elseif autoTargetSelector then spellArray[k].ts:tick() spellArray[k].target = spellArray[k].ts.target
                else
                    local target, distance = findHeroNearestMouse()
                    if distance >= v.range then target = nil end
                    spellArray[k].target = target
                end
                if spellArray[k].target and not hasShield(spellArray[k].target) then
                    --BasicAttack
                    if k then PrintChat(k) end
                    if spellArray[k].target then PrintChat(spellArray[k].target.charName)       end
                    if lastBasicAttackTarget then PrintChat(lastBasicAttackTarget.charName)  end
                    if k == BasicAttackSpell and spellArray[k].target and (not lastBasicAttackTarget or lastBasicAttackTarget.name ~= spellArray[k].target.name) then
                        local distance = player:GetDistance(spellArray[k].target)
                        if distance > 0 and distance < player.range or 50 then
                            player:Attack(spellArray[k].target)
                            lastBasicAttackTarget = spellArray[k].target
                        end
                    end
                    --CastSection  if v.speed then spellArray[k].tp = TargetPrediction:new(v.range, v.speed, v.delay, v.width) end
                    if (not v.damage or (v.damage(spellArray[k].target) > spellArray[k].target.health and (not healthPrediction or (healthPrediction > 0 and v.damage(spellArray[k].target) > healthPrediction)))) then
                        if minions then PrintChat("Minion Collision") end
                        if prediction and not minions then
                            CastSpell(spellArray[k].spellKey, prediction.x, prediction.z)
                            if v.delay then continueTick = GetTickCount() + v.delay end
                        elseif not v.speed and spellArray[k].target.visible then
                            CastSpell(spellArray[k].spellKey, spellArray[k].target)
                            if player:CanUseSpell(spellArray[k].spellKey) == READY then
                                CastSpell(spellArray[k].spellKey, spellArray[k].target.x, spellArray[k].target.z)
                            end
                            if v.delay then continueTick = GetTickCount() + v.delay end
                        end
                    end
                end
            end
        end
    end
end

function OnWndMsg(msg, key)
    if msg == KEY_DOWN then
        if key == spamKey then
            for k, v in pairs(spellArray) do
                if v.spammable then spellArray[k].active = true end
            end
        elseif key == triggerKeyQ and spellArray.q then spellArray.q.active = true
        elseif key == triggerKeyW and spellArray.w then spellArray.w.active = true
        elseif key == triggerKeyE and spellArray.e then spellArray.e.active = true
        elseif key == toggleKeyR and spellArray.r then
            if not spellArray.r.active then PrintChat("Ult activated") spellArray.r.active = true
            else PrintChat("Ult deactivated") spellArray.r.active = false
            end
        end
    elseif key == triggerKeyQ and spellArray.q then spellArray.q.active = false
    elseif key == triggerKeyW and spellArray.w then spellArray.w.active = false
    elseif key == triggerKeyE and spellArray.e then spellArray.e.active = false
    elseif key == spamKey then
        for k, v in pairs(spellArray) do
            if v.spammable then spellArray[k].active = false end
        end
    else
        if key == spamKey then
            for k, v in pairs(spellArray) do
                if v.spammable then spellArray[k].target = nil end
            end
        end
    end
end

function OnDraw()
    tp:tick()
    for k, v in pairs(spellArray) do
        if v.speed then
            if showPredictions and v.target then
                local p = tp:GetPrediction(v.target.name, v.range, v.speed, v.delay, nil, smoothness)
                if p then DrawCircle(p.x, v.target.y, p.z, 100, 0xFFFFFF) end
            end
        end
        if showSkillShotRange then
            DrawCircle(player.x, player.y, player.z, spellArray[k].range, 0xFFFFFF)
        end
    end
end

function OnLoad()
    if player.charName == "DrMundo" then
        spellArray = {
            q = { spellKey = _Q, range = 1100, speed = 2, delay = 300, width = disableMinionCollisionDetection and nil or 200, spammable = true },
        }
    elseif player.charName == "LeeSin" then
        spellArray = {
            q = { spellKey = _Q, range = 1100, speed = 1.8, delay = 300, width = disableMinionCollisionDetection and nil or 200, spammable = true },
        }
    elseif player.charName == "Olaf" then
        spellArray = {
            q = { spellKey = _Q, range = 1000, speed = 1.6, delay = 300, width = disableMinionCollisionDetection and nil or 200, spammable = true },
        }
    elseif player.charName == "Blitzcrank" then
        spellArray = {
            q = { spellKey = _Q, range = 925, speed = 1.7, delay = 300, width = disableMinionCollisionDetection and nil or 200, spammable = true },
            e = { spellKey = _E, range = 125, spammable = true },
            r = { spellKey = _R, range = 600, damage = function(target) return player:CalcMagicDamage(target, 125 + 125 * player:GetSpellData(_R).level + player.ap) end, active = true }
        }
    elseif player.charName == "Amumu" then
        spellArray = {
            q = { spellKey = _Q, range = 1100, speed = 2, delay = 300, width = disableMinionCollisionDetection and nil or 200, spammable = true },
        }
    elseif player.charName == "Ezreal" then
        spellArray = {
            q = { spellKey = _Q, range = 1000, speed = 1.85, delay = 281, width = disableMinionCollisionDetection and nil or 250, spammable = true },
            w = { spellKey = _W, range = 850, speed = 1.55, delay = 265, spammable = true },
            e = { spellKey = _E, range = 1200, delay = 266, spammable = true },
            r = { spellKey = _R, range = 6000, speed = 2, delay = 1030, damage = function(target) return player:CalcMagicDamage(target, 180 + 150 * player:GetSpellData(_R).level + player.totalDamage + 0.9 * player.ap) end },
        }

    elseif player.charName == "Gragas" then
        spellArray = {
            q = { spellKey = _Q, range = 1100, speed = 1, delay = 300, spammable = true },
        }
    elseif player.charName == "Lux" then
        spellArray = {
            q = { spellKey = _Q, range = 1000, speed = 1.17, delay = 300, width = disableMinionCollisionDetection and nil or 200, spammable = true },
            e = { spellKey = _E, range = 1100, speed = 1.24, delay = 300, spammable = true },
            r = { spellKey = _R, range = 3000, speed = 0, delay = 500, damage = function(target) return player:CalcMagicDamage(target, 190 + 95 * player:GetSpellData(_R).level + 0.70 * player.ap) end, active = true },
        }
    elseif player.charName == "Morgana" then
        spellArray = {
            q = { spellKey = _Q, range = 1300, speed = 1.2, delay = 300, width = disableMinionCollisionDetection and nil or 200, spammable = true },
        }
    elseif player.charName == "Nidalee" then
        spellArray = {
            q = { spellKey = _Q, range = 1500, speed = 1.3, delay = 300, width = disableMinionCollisionDetection and nil or 200, spammable = true },
        }
    elseif player.charName == "Sivir" then
        spellArray = {
            q = { spellKey = _Q, range = 1000, speed = 1.33, delay = 300, spammable = true },
        }
    elseif player.charName == "Anivia" then
        spellArray = {
            q = { spellKey = _Q, range = 1100, speed = 0.845, delay = 300, spammable = true },
        }
    elseif player.charName == "Kennen" then
        spellArray = {
            q = { spellKey = _Q, range = 1050, speed = 1.7, delay = 300, width = disableMinionCollisionDetection and nil or 200, spammable = true },
        }
    elseif player.charName == "Ahri" then
        spellArray = {
            q = { spellKey = _Q, range = 880, speed = 1.17, delay = 300, width = nil, spammable = true },
            e = { spellKey = _E, range = 975, speed = 1.2, delay = 300, width = disableMinionCollisionDetection and nil or 200, spammable = true }
        }
    else
        PrintChat(" >> AIOSkillShots: You are not playing a supported champion!")
        OnWndMsg, OnTick, OnDraw = function() end, function() end, function() end
        return
    end
    for k, v in pairs(spellArray) do
        spellArray[k].ts = TargetSelector:new(v.targetMode or 1, v.range, v.damageType or nil) --you can set targetMode and damagetype in the array
    end
    tp = TargetPrediction:new()
    PrintChat(string.format(" >> AIOSkillShots: %s detected!", player.charName))
end
